package com.epoint.springsecuritylearning.user.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.epoint.springsecuritylearning.exception.LoginUserDetails;
import com.epoint.springsecuritylearning.exception.UserNullException;
import com.epoint.springsecuritylearning.user.domain.SysUser;
import com.epoint.springsecuritylearning.user.service.UserService;
import com.epoint.springsecuritylearning.util.JwtUtil;
import com.epoint.springsecuritylearning.util.ResponseResult;
import io.jsonwebtoken.Claims;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import java.util.*;

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private RedisTemplate redisTemplate;


    @Override
    public ResponseResult login(SysUser user) {
        // 校验，成功的话 生成JWT
        //AuthenticationManager authenticate进行用户认证
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user.getLoginName(),user.getPassword());
        Authentication authenticate = authenticationManager.authenticate(authenticationToken);
        //如果认证没通过，给出对应的提示，会被Security异常拦截器捕捉
        if(authenticate == null){
            throw new UserNullException("登录失败");
        }
        LoginUserDetails principal = (LoginUserDetails) authenticate.getPrincipal();
        // 生成JWT 通过UserId生成JWT
        /**
         * 这里有个细节：如果我们用UserId作为redis的key的话有个弊端
         * 如果一个账号，2个电脑同时登录，那么他们其中一个人只要退出，则会影响另外一个人
         * 解决：用UUID来替代userid作为redis的key即可
         */
        String jwt = JwtUtil.createJWT(principal.getUser().getUserId());
        Map<String,String> map = new HashMap<>();
        map.put("token",jwt);

        // 塞入redis
        String json = JSONObject.toJSONString(principal);
        stringRedisTemplate.opsForValue().set("login:" + principal.getUser().getUserId(), json);
        redisTemplate.opsForValue().set("ss", principal);
        return new ResponseResult(200, "登录成功", map);
    }

    @Override
    public ResponseResult logout() {
//        if(StringUtils.isBlank(token)){
//            return new ResponseResult(300, "登出错误");
//        }
//
//        // 通过token查询到redis
//        Claims claims;
//        try {
//            claims = JwtUtil.parseJWT(token);
//        } catch (Exception e) {
//            return new ResponseResult(300, "登出错误");
//        }

//        String userID = claims.getSubject();


        /**
         * 重点：上面重复动作了，因为登出的时候会代入token，他会被拦截，
         * 会被拦截器之后会把正确的当前人的信息封装到Authentication塞入 SecurityContext之中，在本次请求（线程ThreadLocal）里都可以直接获取
         */
        //获取SecurityContextHolder中的用户id
        UsernamePasswordAuthenticationToken authentication = (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
        LoginUserDetails loginUser = (LoginUserDetails) authentication.getPrincipal();
        String userId = loginUser.getUser().getUserId();
        // 删除redis当前人员的记录
        Boolean delete = stringRedisTemplate.delete("login:" + userId);
        if(delete){
            return new ResponseResult(200, "登出成功");
        }else{
            return new ResponseResult(300, "登出错误");
        }
    }
}
